package cn.iocoder.yudao.framework.license;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.digest.DigestUtil;
import cn.iocoder.yudao.framework.common.exception.Assertions;
import cn.iocoder.yudao.framework.common.exception.KnownServiceException;
import cn.iocoder.yudao.framework.constant.ConfigConstants;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.ObjectInputStream;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Objects;

/**
 * <pre>
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 *  许可证处理器
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 * </pre>
 *
 * @author 山野羡民（1032694760@qq.com）
 * @since 2025/04/05
 * @link <a href="https://gitee.com/stephenson37/tg-license">tg-license</a>
 * @link <a href="https://gitee.com/smartboot/smart-license">smart-license</a>
 */
@Slf4j
public class LicenseHandler {
    private static final String FILE_NAME = "LICENSE.bin";
    private static final String FILE_HEADER = "LIYUJIANG";

    public static LicenseEntity checkLicense() throws KnownServiceException {
        try {
            byte[] bytes = doLoadAndParse();
            try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
                 ObjectInputStream ois = new ObjectInputStream(bis)
            ) {
                LicenseEntity licenseEntity = (LicenseEntity) ois.readObject();
                //System.out.println("entity=" + licenseEntity);
                Assertions.notNull(licenseEntity, "许可证文件解析失败，请联系开发者处理");
                Assertions.isTrue(licenseEntity.getSignature().equals(doSignature(licenseEntity)), "许可证文件签名无效，请联系开发者处理");
                Assertions.isTrue(LocalDateTime.now().isBefore(licenseEntity.getExpireTime()), "许可证文件过期，请联系开发者处理");
                return licenseEntity;
            }
        } catch (KnownServiceException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace(System.out);
            throw new KnownServiceException("许可证文件读取出错，请联系开发者处理");
        }
    }

    private static byte[] doLoadAndParse() {
        File file = new File(ConfigConstants.SYSTEM_USER_DIR, FILE_NAME);
        //System.out.println("path=" + file.getAbsolutePath());
        byte[] rawBytes = FileUtil.readBytes(file);
        Assertions.isFalse(rawBytes == null || rawBytes.length == 0, "许可证文件没有数据，请联系开发者处理");
        byte[] exceptHeader = FILE_HEADER.getBytes();
        //System.out.println("exceptHeader=" + Arrays.toString(exceptHeader));
        byte[] actualHeader = new byte[exceptHeader.length];
        System.arraycopy(rawBytes, 0, actualHeader, 0, exceptHeader.length);
        //System.out.println("actualHeader=" + Arrays.toString(actualHeader));
        Assertions.isTrue(Arrays.equals(exceptHeader, actualHeader), "许可证文件格式无效，请联系开发者处理");
        byte[] publicKeyLengthBytes = new byte[4];
        System.arraycopy(rawBytes, exceptHeader.length, publicKeyLengthBytes, 0, publicKeyLengthBytes.length);
        int publicKeyLength = Convert.bytesToInt(publicKeyLengthBytes);
        //System.out.println("publicKeyLength=" + publicKeyLength);
        byte[] publicKeyBytes = new byte[publicKeyLength];
        System.arraycopy(rawBytes, exceptHeader.length + publicKeyLengthBytes.length, publicKeyBytes, 0, publicKeyBytes.length);
        byte[] serializeBytes = new byte[rawBytes.length - exceptHeader.length - publicKeyLengthBytes.length - publicKeyBytes.length];
        System.arraycopy(rawBytes, exceptHeader.length + publicKeyLengthBytes.length + publicKeyBytes.length, serializeBytes, 0, serializeBytes.length);
        //System.out.println("serializeBytesLength=" + serializeBytes.length);
        RSA rsa = new RSA(null, publicKeyBytes);
        return rsa.decrypt(serializeBytes, KeyType.PublicKey);
    }

    private static String doSignature(LicenseEntity entity) {
        String str = entity.getProgramName();
        str += "_" + entity.getHolderName();
        if (Objects.nonNull(entity.getIssueTime())) {
            str += "_" + entity.getIssueTime();
        }
        if (Objects.nonNull(entity.getExpireTime())) {
            str += "_" + entity.getExpireTime();
        }
        return DigestUtil.sha256Hex(str);
    }

}
